import 'dart:io';

import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart';
import 'dart:math' as Math;

class GPSPoint {
  final double lat;
  final double lng;

  GPSPoint({required this.lat, required this.lng});
}

class CurrGeo {
  final double? lat;
  final double? lng;
  final String? country;
  final String? province;
  final String? city;
  final String? area;
  final String? landmark;

  CurrGeo({
    this.lat,
    this.lng,
    this.country,
    this.province,
    this.city,
    this.area,
    this.landmark
});
}

class GeoUtils {
  static Future<bool> checkPermission() async {
    var p = await Geolocator.checkPermission();
    if (p == LocationPermission.denied || p == LocationPermission.deniedForever) {
      return false;
    }
    return true;
  }

  static GPSPoint? lastPoint;
  static int lastPointNs = 0;

  static Future<GPSPoint?> getCurrentPosition() async {
    if (lastPointNs > DateTime.now().millisecondsSinceEpoch - 30000) {
      return lastPoint;
    }
    bool serviceEnabled;
    LocationPermission permission;
    serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      return null;
    }
    permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        return null;
      }
    }
    if (permission == LocationPermission.deniedForever) {
      return null;
    }
    var geo = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.bestForNavigation);
    lastPointNs = DateTime.now().millisecondsSinceEpoch;
    if (Platform.isIOS) {
      lastPoint = WGS84_to_GCJ02(wgLat: geo.latitude, wgLng: geo.longitude);
      return lastPoint;
    } else {
      lastPoint = GPSPoint(lat: geo.latitude, lng: geo.longitude);
      return lastPoint;
    }
  }

  static Placemark? lastRegeo;
  static int lastRegeoMs = 0;

  static Future<Placemark?> regeo({required double lat, required double lng}) async {
    if (lastRegeoMs > DateTime.now().millisecondsSinceEpoch - 30000) {
      return lastRegeo;
    }
    var res = await placemarkFromCoordinates(lat, lng, localeIdentifier: 'zh_CN');
    if (res.isNotEmpty) {
      lastRegeoMs = DateTime.now().millisecondsSinceEpoch;
      lastRegeo = res[0];
    }
    return lastRegeo;
  }

  static Future<CurrGeo?> getCurrGeo() async {
    var latLng = await getCurrentPosition();
    if (latLng == null) {
      return null;
    }
    var geo = await regeo(lat: latLng.lat, lng: latLng.lng);
    if (geo == null) {
      return null;
    }
    return CurrGeo(lat: latLng.lat, lng: latLng.lng, country: geo.country, province: geo.administrativeArea, city: geo.locality,area: geo.subLocality, landmark: geo.thoroughfare);
  }

  static const double x_pi = Math.pi * 3000.0 / 180.0;

  //克拉索天斯基椭球体参数值
  static const double a = 6378245.0;

  //第一偏心率
  static const double ee = 0.00669342162296594323;

  static GPSPoint WGS84_to_GCJ02({required double wgLat, required double wgLng}) {
    if (outOfChina(wgLat, wgLng)) {
      return GPSPoint(lat: wgLat, lng: wgLng);
    }
    double dLat = transformLat(wgLng - 105.0, wgLat - 35.0);
    double dLng = transformLon(wgLng - 105.0, wgLat - 35.0);
    double radLat = wgLat / 180.0 * Math.pi;
    double magic = Math.sin(radLat);
    magic = 1 - ee * magic * magic;
    double sqrtMagic = Math.sqrt(magic);
    dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * Math.pi);
    dLng = (dLng * 180.0) / (a / sqrtMagic * Math.cos(radLat) * Math.pi);
    return GPSPoint(lat: wgLat + dLat, lng: wgLng + dLng);
  }

  static List<double> transform(double wgLat, double wgLon) {
    List<double> latlng = [wgLat, wgLon];
    if (outOfChina(wgLat, wgLon)) {
      return latlng;
    }
    double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
    double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
    double radLat = wgLat / 180.0 * Math.pi;
    double magic = Math.sin(radLat);
    magic = 1 - ee * magic * magic;
    double sqrtMagic = Math.sqrt(magic);
    dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * Math.pi);
    dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * Math.pi);
    latlng[0] = wgLat + dLat;
    latlng[1] = wgLon + dLon;
    return latlng;
  }

  static bool outOfChina(double lat, double lon) {
    if (lon < 72.004 || lon > 137.8347) {
      return true;
    }
    if (lat < 0.8293 || lat > 55.8271) {
      return true;
    }
    return false;
  }

  static double transformLat(double x, double y) {
    double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(x.abs());
    ret += (20.0 * Math.sin(6.0 * x * Math.pi) + 20.0 * Math.sin(2.0 * x * Math.pi)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(y * Math.pi) + 40.0 * Math.sin(y / 3.0 * Math.pi)) * 2.0 / 3.0;
    ret += (160.0 * Math.sin(y / 12.0 * Math.pi) + 320 * Math.sin(y * Math.pi / 30.0)) * 2.0 / 3.0;
    return ret;
  }

  static double transformLon(double x, double y) {
    double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(x.abs());
    ret += (20.0 * Math.sin(6.0 * x * Math.pi) + 20.0 * Math.sin(2.0 * x * Math.pi)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(x * Math.pi) + 40.0 * Math.sin(x / 3.0 * Math.pi)) * 2.0 / 3.0;
    ret += (150.0 * Math.sin(x / 12.0 * Math.pi) + 300.0 * Math.sin(x / 30.0 * Math.pi)) * 2.0 / 3.0;
    return ret;
  }
}
